#include "pn.h"
#include "util.h"

extern vector <string> n2s;
extern map <string,int> dict,gaz;

// Features

extern bool _CAP, _DICT, _GAZ, _POS, _G, _MENT, _ING;

extern vector <bool> fdi, fga,fcap,fment,fing;
extern vector <string> fg;

extern int BEAM;

posNeg:: posNeg(const vector <int> &pstr, const vector <int> &ppt,  const vector <int> &pbw, const vector <int> &paw,  const vector <int> &pbp, const vector <int> &pap,const vector <int> &pntw, const vector <int> &pntp, bool ef) {
   str = pstr;
   pt = ppt;
   bw=pbw;
   aw=paw;
   bp=pbp;
   ap=pap;

   //nt = pnt;
   //ntst = pntst;
   //nten = pnten;
   ntw=pntw;
   ntp=pntp;

   /*
   for (int i=0; i<bw.size(); ++i) {
      strW.push_back(bw[i]);
   }
   for (int i=0; i<str.size(); ++i) {
      strW.push_back(str[i]);
   }
   for (int i=0; i<aw.size(); ++i) {
      strW.push_back(aw[i]);
   }

   for (int i=0; i<bp.size(); ++i) {
      ptW.push_back(bp[i]);
   }
   for (int i=0; i<pt.size(); ++i) {
      ptW.push_back(pt[i]);
   }
   for (int i=0; i<ap.size(); ++i) {
      ptW.push_back(ap[i]);
   }*/

   /*
   vector <bool> rem(str.size(),true);
   vector <int> tw = str;
   vector <int> tp = pt;
   for (int i=0; i<nt.size(); ++i) {
      tw[ntst[i]] = nt[i];
      tp[ntst[i]] = nt[i];
      for (int j=ntst[i]+1; j<=nten[i]; ++j) rem[j]=false;
   }
   for (int i=0; i<tw.size(); ++i) {
      if (rem[i]) ntw.push_back(tw[i]);
   }
   for (int i=0; i<tp.size(); ++i) {
      if (rem[i]) ntp.push_back(tp[i]);
   }
   */
   /*
   for (int i=0; i<bw.size(); ++i) {
      ntwW.push_back(bw[i]);
   }
   for (int i=0; i<ntw.size(); ++i) {
      ntwW.push_back(ntw[i]);
   }
   for (int i=0; i<aw.size(); ++i) {
      ntwW.push_back(aw[i]);
   }

   for (int i=0; i<bp.size(); ++i) {
      ntpW.push_back(bp[i]);
   }
   for (int i=0; i<ntp.size(); ++i) {
      ntpW.push_back(ntp[i]);
   }
   for (int i=0; i<ap.size(); ++i) {
      ntpW.push_back(ap[i]);
   }*/


   sk = -1;
   cap = false;
   allCap = true;
   di = false;
   allDi = true;
   ga = false;
   allGa = true;
   ment=false;
   ing=false;
   
   //ef=false; // removes extra features

   extraFeatures = ef;
   if (ef) {
      for (int i=0; i<str.size(); ++i) {
	 if (_CAP){
	    if (fcap[str[i]]) cap=true;
	    else allCap = false;
	 }
	 if (_DICT) {
            if (fdi[str[i]]) di = true;
	    else allDi = false; 
	 }
	 if (_GAZ) {
            if (fga[str[i]]) ga = true;
	    else allGa = false; 
	 }
	 if (_MENT) {
            if (fment[str[i]]) ment = true;
	 }
	 if (_ING) {
            if (fing[str[i]]) ing = true;
	 }
      }
      if (_G) { // g-feature of the head word (last word)
	 assert(str.size() > 0);
	 g = fg[str[str.size()-1]];
      }
   }
   /*
   for (int i=0; i<str.size(); ++i) {
      cout<<n2s[str[i]]<<" ";
   }
   cout<<"\n";
   for (int i=0; i<strW.size(); ++i) {
      cout<<n2s[strW[i]]<<" ";
   }
   cout<<"\n";
   for (int i=0; i<ntw.size(); ++i) {
      cout<<n2s[ntw[i]]<<" ";
   }
   cout<<"\n\n\n";
   */
}


void posNeg::writeStr(ostream &out) const {
   for (int j=0; j<str.size(); ++j) {
 //     string s = n2s[str[j]];
   //   out<<s<<" ";
   }
 //  out<<"\n";
   for (int j=0; j<ntw.size(); ++j) {
     // string s = n2s[ntw[j]];
     // out<<s<<" ";
   }
   //out<<"\n";
}


double pn::relationKernel(const posNeg &p1, const posNeg &p2) const {
   double d=0;
   d += ncsUn(p1.str, p2.str); 
   //d += ncsUn(p1.strW, p2.strW); 
   
   d += ncsUn(p1.bw, p2.bw); 
   d += ncsUn(p1.ntw, p2.ntw); 
   d += ncsUn(p1.aw, p2.aw); 
   
   //d += ncsUn(p1.ntwW, p2.ntwW); 
   //d += ncsUn(p1.ntpW, p2.ntpW); 
   //

   /*d += ncsUn(p1.pt, p2.pt); 
   d += ncsUn(p1.bp, p2.bp); 
   d += ncsUn(p1.ntp, p2.ntp); 
   d += ncsUn(p1.ap, p2.ap); 
   */

   if (BEAM > 1) { // otherwise pipeline
   for (int i=0; i<p1.path.size(); ++i) {
      for (int j=0; j<p2.path.size(); ++j) {
	 if (p1.path[i]==p2.path[j])  {
	    /*
	    double prob=1;
	    for (int a=0; a<p1.pathProb[i].size(); ++a) {
	       prob *= p1.pathProb[i][a];
	    }
	    for (int a=0; a<p2.pathProb[j].size(); ++a) {
	       prob *= p2.pathProb[j][a];
	    }
	    d += prob;
	    */
	    ++d;
	 }
      }
   }
   }

   return d;
}

int pn::add(posNeg &eg) {
   assert(eg.str.size() > 0);
   int j=eg.str[0];

   if (j > ponePresent.size()) {
      ponePresent.resize(j*2);
   }

   int i;
   for (i=0; i<ponePresent[j].size(); ++i) {
      if (eg==pone[ponePresent[j][i]]) {
	 return ponePresent[j][i];
      }
   }
      
   double ps=0;
   if (!eg.extraFeatures) {
      ps += relationKernel(eg,eg);
      if (ps==0) ps=1; // not sure
   }
   else {
      ps += ncsUn(eg.str,eg.str);
      if (_CAP) ps +=2;
      if (_DICT) ps +=2;
      if (_GAZ) ps +=2;
      if (_POS) ps +=1;
      if (_G) ps +=1;
      if (_MENT) ps +=1;
      if (_ING) ps +=1;
      ps += 4; // bw,bp,aw,ap
   }
   eg.putSelfK(ps);
   pone.push_back(eg);
   ponePresent[j].push_back(pone.size()-1);
   return pone.size()-1;
}

double pn::kernelUn(pn &tsw, int i, int j) {
   assert(i < tsw.pone.size() && j < pone.size());
      
   map <int,double>::iterator itr;

   itr = tsw.pone[i].k.find(j);

   if (itr == tsw.pone[i].k.end()){

    double d=0;
    assert(tsw.pone[i].extraFeatures==pone[j].extraFeatures);
	 if (!tsw.pone[i].extraFeatures) {
	    d += relationKernel(tsw.pone[i],pone[j]);
	 }
	 else {
	    d += ncsUn(tsw.pone[i].str, pone[j].str); 
	    if (tsw.pone[i].bw==pone[j].bw) ++d;
	    if (tsw.pone[i].bp==pone[j].bp) ++d;
	    if (tsw.pone[i].aw==pone[j].aw) ++d;
	    if (tsw.pone[i].ap==pone[j].ap) ++d;
	    
	    if (_CAP) {
	       if (tsw.pone[i].cap==pone[j].cap) ++d;
	       if (tsw.pone[i].allCap==pone[j].allCap) ++d;
	    }
	    if (_DICT) {
	       if (tsw.pone[i].di==pone[j].di) ++d;
	       if (tsw.pone[i].allDi==pone[j].allDi) ++d;
	    }
	    if (_GAZ) {
	       if (tsw.pone[i].ga==pone[j].ga) ++d;
	       if (tsw.pone[i].allGa==pone[j].allGa) ++d;
	    }
	    if (_POS && tsw.pone[i].pt==pone[j].pt) ++d;
	    if (_G && tsw.pone[i].g==pone[j].g) ++d;
	    if (_MENT && tsw.pone[i].ment==pone[j].ment) ++d;
	    if (_ING && tsw.pone[i].ing==pone[j].ing) ++d;
	 }

	 if (tsw.pone[i].k.size() >= 3000) tsw.pone[i].k.clear();
	    
	 tsw.pone[i].k[j] = d;

	 return d;
   }
   else return itr->second;
}

double pn::kernelUn(int i, int j) {
   if (i < j) return kernelUn(*this,i,j);
   else return kernelUn(*this,j,i);
}

void pn::writeStr(ostream &out, int i) const {
   /*
   for (int j=0; j<pone[i].str.size(); ++j) out<<n2s[pone[i].str[j]]<<" ";
   out<<"\n";
   for (int j=0; j<pone[i].ntw.size(); ++j) out<<n2s[pone[i].ntw[j]]<<" ";
   out<<"\n";
   */
}

double pn::ncsUn(const vector <int> &x, const vector <int> &y) const {
   if (x.size()==0 || y.size()==0) return 0;
   int N=5; //5// 8 string kernel parameter (3)
   double LAMBDA=0.25; // 0.25: CLang, 0.75:Geo string kernel parameter

   double r=0;

   double lambda=LAMBDA; // lambda==1 will give the number of common subsequences
   double lambdaSq = lambda*lambda;

   if (x.size() < N) N = x.size();
   if (y.size() < N) N = y.size();
   
   vector <vector <double> > k(x.size(), vector <double> (y.size(), 0));
   vector <double> cs(N+1, 0);

   // length one
   cs[1] = 0;
   for (int i=0; i<x.size(); ++i) {
      for (int j=0; j<y.size(); ++j) {
	 if (x[i]==y[j]) {
	    k[i][j] = lambdaSq;
	    cs[1] += k[i][j];
	 }
      }
   }
   cs[1] /= lambdaSq;

   // > 1 lengths
   for (int l=2; l<=N; ++l) {
      vector <vector <double> > S(x.size()+1, vector <double> (y.size()+1, 0));
      cs[l] = 0;

      for (int i=0; i<x.size(); ++i) {
         for (int j=0; j<y.size(); ++j) {
	    S[i+1][j+1] = k[i][j] + lambda*S[i][j+1] + lambda*S[i+1][j] - lambdaSq*S[i][j];
	    if (x[i]==y[j]) {
	       k[i][j] = lambdaSq*S[i][j];
	       cs[l] += k[i][j];
	    }
	 }
      }
      cs[l] /= power(lambda, 2*l);
   }
   for (int l=1; l<=N; ++l) {
      r += cs[l];
   }
   return r;
}

